<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<meta name="keywords" content="Seata,Nacos,分布式事务,spring" />
	<meta name="description" content="本文讲述如何通过AOP动态创建/关闭Seata分布式事务" />
	<!-- 网页标签标题 -->
	<title>通过AOP动态创建/关闭Seata分布式事务</title>
  <link rel="shortcut icon" href="/img/seata_logo_small.jpeg"/>
	<link rel="stylesheet" href="/build/blogDetail.css" />
</head>
<body>
	<div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/seata_logo.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/system/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a href="/zh-cn/index.html" target="_self">首页</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">文档</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/docs/developers/developers_dev.html" target="_self">开发者</a></li><li class="menu-item menu-item-normal menu-item-normal-active"><a href="/zh-cn/blog/index.html" target="_self">博客</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/community/index.html" target="_self">社区</a></li><li class="menu-item menu-item-normal"><a href="/zh-cn/blog/download.html" target="_self">下载</a></li></ul></div></div></header><section class="blog-content markdown-body"><h1>通过AOP动态创建/关闭Seata分布式事务</h1>
<p>本文作者：FUNKYE(陈健斌),杭州某互联网公司主程。</p>
<h1>前言</h1>
<p>通过GA大会上滴滴出行的高级研发工程陈鹏志的在滴滴两轮车业务中的实践,发现动态降级的必要性是非常的高,所以这边简单利用spring boot aop来简单的处理降级相关的处理,这边非常感谢陈鹏志的分享!</p>
<p>可利用此demo<a href="https://gitee.com/itCjb/springboot-dubbo-mybatisplus-seata">项目地址</a></p>
<p>通过以下代码改造实践.</p>
<h2>准备工作</h2>
<p>​	1.创建测试用的TestAspect:</p>
<pre><code class="language-java"><span class="hljs-keyword">package</span> org.test.config;

<span class="hljs-keyword">import</span> java.lang.reflect.Method; 

<span class="hljs-keyword">import</span> org.apache.commons.lang3.StringUtils;
<span class="hljs-keyword">import</span> org.aspectj.lang.JoinPoint;
<span class="hljs-keyword">import</span> org.aspectj.lang.annotation.AfterReturning;
<span class="hljs-keyword">import</span> org.aspectj.lang.annotation.AfterThrowing;
<span class="hljs-keyword">import</span> org.aspectj.lang.annotation.Aspect;
<span class="hljs-keyword">import</span> org.aspectj.lang.annotation.Before;
<span class="hljs-keyword">import</span> org.aspectj.lang.reflect.MethodSignature;
<span class="hljs-keyword">import</span> org.slf4j.Logger;
<span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Component;

<span class="hljs-keyword">import</span> io.seata.core.context.RootContext;
<span class="hljs-keyword">import</span> io.seata.core.exception.TransactionException;
<span class="hljs-keyword">import</span> io.seata.tm.api.GlobalTransaction;
<span class="hljs-keyword">import</span> io.seata.tm.api.GlobalTransactionContext;

<span class="hljs-meta">@Aspect</span>
<span class="hljs-meta">@Component</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestAspect</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> Logger logger = LoggerFactory.getLogger(TestAspect.class);
    
    <span class="hljs-meta">@Before</span>(<span class="hljs-string">"execution(* org.test.service.*.*(..))"</span>)
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">before</span><span class="hljs-params">(JoinPoint joinPoint)</span> <span class="hljs-keyword">throws</span> TransactionException </span>{
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        logger.info(<span class="hljs-string">"拦截到需要分布式事务的方法,"</span> + method.getName());
        <span class="hljs-comment">// 此处可用redis或者定时任务来获取一个key判断是否需要关闭分布式事务</span>
        <span class="hljs-comment">// 模拟动态关闭分布式事务</span>
        <span class="hljs-keyword">if</span> ((<span class="hljs-keyword">int</span>)(Math.random() * <span class="hljs-number">100</span>) % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>) {
            GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
            tx.begin(<span class="hljs-number">300000</span>, <span class="hljs-string">"test-client"</span>);
        } <span class="hljs-keyword">else</span> {
            logger.info(<span class="hljs-string">"关闭分布式事务"</span>);
        }
    }

    <span class="hljs-meta">@AfterThrowing</span>(throwing = <span class="hljs-string">"e"</span>, pointcut = <span class="hljs-string">"execution(* org.test.service.*.*(..))"</span>)
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doRecoveryActions</span><span class="hljs-params">(Throwable e)</span> <span class="hljs-keyword">throws</span> TransactionException </span>{
        logger.info(<span class="hljs-string">"方法执行异常:{}"</span>, e.getMessage());
        <span class="hljs-keyword">if</span> (!StringUtils.isBlank(RootContext.getXID()))
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
    }

    <span class="hljs-meta">@AfterReturning</span>(value = <span class="hljs-string">"execution(* org.test.service.*.*(..))"</span>, returning = <span class="hljs-string">"result"</span>)
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">afterReturning</span><span class="hljs-params">(JoinPoint point, Object result)</span> <span class="hljs-keyword">throws</span> TransactionException </span>{
        logger.info(<span class="hljs-string">"方法执行结束:{}"</span>, result);
        <span class="hljs-keyword">if</span> ((Boolean)result) {
            <span class="hljs-keyword">if</span> (!StringUtils.isBlank(RootContext.getXID())) {
                logger.info(<span class="hljs-string">"分布式事务Id:{}"</span>, RootContext.getXID());
                GlobalTransactionContext.reload(RootContext.getXID()).commit();
            }
        }
    }

}
</code></pre>
<p>请注意上面的包名可改为你自己的service包名:</p>
<p>​	2.改动service代码:</p>
<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">seataCommit</span><span class="hljs-params">()</span> </span>{
        testService.Commit();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
</code></pre>
<p>因为异常跟返回结果我们都会拦截,所以这边可以trycatch或者直接让他抛异常来拦截也行,或者直接判断返回结果,比如你的业务代码code=200为成功,那么就commit,反之在拦截返回值那段代码加上rollback;</p>
<h1>进行调试</h1>
<p>​	1.更改代码主动抛出异常</p>
<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">seataCommit</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">try</span> {
            testService.Commit();
            <span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span> / <span class="hljs-number">0</span>;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> handle exception</span>
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException();
        }
    }
</code></pre>
<p>​	查看日志:</p>
<pre><code class="language-java"><span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.386</span>  INFO <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] org.test.controller.TestController       : 拦截到需要分布式事务的方法,seataCommit
<span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.489</span>  INFO <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] i.seata.tm.api.DefaultGlobalTransaction  : Begin <span class="hljs-keyword">new</span> global transaction [<span class="hljs-number">192.168</span>.14.67:<span class="hljs-number">8092</span>:<span class="hljs-number">2030765910</span>]
<span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.489</span>  INFO <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] org.test.controller.TestController       : 创建分布式事务完毕<span class="hljs-number">192.168</span>.14.67:<span class="hljs-number">8092</span>:<span class="hljs-number">2030765910</span>
<span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.709</span>  INFO <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] org.test.controller.TestController       : 方法执行异常:<span class="hljs-keyword">null</span>
<span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.885</span>  INFO <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] i.seata.tm.api.DefaultGlobalTransaction  : [<span class="hljs-number">192.168</span>.14.67:<span class="hljs-number">8092</span>:<span class="hljs-number">2030765910</span>] rollback status: Rollbacked
<span class="hljs-number">2019</span>-<span class="hljs-number">12</span>-<span class="hljs-number">23</span> <span class="hljs-number">11</span>:<span class="hljs-number">57</span>:<span class="hljs-number">55.888</span> ERROR <span class="hljs-number">23952</span> --- [.<span class="hljs-number">0</span>-<span class="hljs-number">28888</span>-exec-<span class="hljs-number">7</span>] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() <span class="hljs-keyword">for</span> servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException] with root cause

</code></pre>
<p>​	可以看到已被拦截也触发了rollback了.</p>
<p>​	2.恢复代码调试正常情况:</p>
<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">seataCommit</span><span class="hljs-params">()</span> </span>{
        testService.Commit();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
</code></pre>
<p>​	查看日志:</p>
<pre><code>2019-12-23 12:00:20.876  INFO 23952 --- [.0-28888-exec-2] org.test.controller.TestController       : 拦截到需要分布式事务的方法,seataCommit
2019-12-23 12:00:20.919  INFO 23952 --- [.0-28888-exec-2] i.seata.tm.api.DefaultGlobalTransaction  : Begin new global transaction [192.168.14.67:8092:2030765926]
2019-12-23 12:00:20.920  INFO 23952 --- [.0-28888-exec-2] org.test.controller.TestController       : 创建分布式事务完毕192.168.14.67:8092:2030765926
2019-12-23 12:00:21.078  INFO 23952 --- [.0-28888-exec-2] org.test.controller.TestController       : 方法执行结束:true
2019-12-23 12:00:21.078  INFO 23952 --- [.0-28888-exec-2] org.test.controller.TestController       : 分布式事务Id:192.168.14.67:8092:2030765926
2019-12-23 12:00:21.213  INFO 23952 --- [.0-28888-exec-2] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.14.67:8092:2030765926] commit status: Committed
</code></pre>
<p>​	可以看到事务已经被提交了.</p>
<h1>总结</h1>
<p>更详细的内容希望希望大家访问以下地址阅读详细文档</p>
<p><a href="https://nacos.io/zh-cn/index.html">nacos官网</a></p>
<p><a href="http://dubbo.apache.org/en-us/">dubbo官网</a></p>
<p><a href="http://seata.io/zh-cn/">seata官网</a></p>
<p><a href="https://www.docker.com/">docker官网</a></p>
</section><footer class="footer-container"><div class="footer-body"><img src="/img/seata_logo_gray.png"/><p class="docsite-power">website powered by docsite</p><div class="cols-container"><div class="col col-12"><h3>愿景</h3><p>Seata 是一款阿里巴巴开源的分布式事务解决方案，致力于在微服务架构下提供高性能和简单易用的分布式事务服务。</p></div><div class="col col-6"><dl><dt>文档</dt><dd><a href="/zh-cn/docs/overview/what-is-seata.html" target="_self">Seata 是什么？</a></dd><dd><a href="/zh-cn/docs/user/quickstart.html" target="_self">快速开始</a></dd><dd><a href="https://github.com/seata/seata.github.io/issues/new" target="_self">报告文档问题</a></dd><dd><a href="https://github.com/seata/seata.github.io" target="_self">在Github上编辑此文档</a></dd></dl></div><div class="col col-6"><dl><dt>资源</dt><dd><a href="/zh-cn/blog/index.html" target="_self">博客</a></dd><dd><a href="/zh-cn/community/index.html" target="_self">社区</a></dd></dl></div></div><div class="copyright"><span>Copyright © 2019 Seata</span></div></div></footer></div></div>
	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
	<script>
		window.rootPath = '';
  </script>
	<script src="/build/blogDetail.js"></script>
	<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?104e73ef0c18b416b27abb23757ed8ee";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
    </script>
</body>
</html>
